package net.uman.framework.modules.search;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import net.uman.entity.IdEntity;

import org.springframework.util.Assert;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

public class MongoDbSearch implements Search {
	private DB db;
	
	public MongoDbSearch() { 
		this("localhost" , 27017);
	}
	
	public MongoDbSearch(String host, int port) {
		try {
			Mongo mongo = new Mongo(host , port);
			db = mongo.getDB("mydb");
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (MongoException e) {
			e.printStackTrace();
		}
	}
	
	public void deleteIndex(String table, String id) throws IOException {
		Criteria criteria = new Criteria();
		criteria.addCondition(new Condition("_id", Condition.EQUALS, id));
		deleteIndex(table, criteria);
	}

	public synchronized void deleteIndex(IdEntity obj) throws IOException {
		Criteria criteria = new Criteria();
		criteria.addCondition(new Condition("_id", Condition.EQUALS, obj.getId()));
		deleteIndex(obj.getClass().getSimpleName(), criteria);
	}
	
	public synchronized void deleteIndex(String table, Criteria criteria) throws IOException {
		DBCollection coll = db.getCollection(table);
		BasicDBObject query = getQuery(criteria);
		coll.remove(query);
	}
	
	public synchronized void createIndex(String table, String id, FieldDesc[] flds) throws IOException {
		DBCollection coll = db.getCollection(table);
		DBObject object = new BasicDBObject();  
		object.put("_id", id);
		for (FieldDesc fld : flds) {
			object.put(fld.getName(), fld.getValue());  
		}
		coll.insert(object);  
	}
	
	public synchronized void closeIndex() throws IOException {		
	}
	
	public List<List<FieldDesc>> queryDate(String table, Criteria criteria) throws IOException {
		List<List<FieldDesc>> docs = new ArrayList<List<FieldDesc>>();
		
		BasicDBObject query = getQuery(criteria);
		
		DBCollection coll = db.getCollection(table);
		DBCursor cur = coll.find(query);
		while(cur.hasNext()){
			DBObject obj = cur.next();
		
			List<FieldDesc> descs = new ArrayList<FieldDesc>();
			for (String key : obj.keySet()) {
				FieldDesc desc = new FieldDesc();
				desc.setName(key);
				desc.setValue(""+obj.get(key));
				
				descs.add(desc);
			}
			docs.add(descs);
		}
		
		return docs;
	}
	
	private BasicDBObject getQuery(Criteria criteria) {
		BasicDBObject query = new BasicDBObject();

		if (criteria.getRelation() == Criteria.AND) {
		} else {
			throw new RuntimeException("Unknown relation: "+criteria.getRelation());
		}
		
		List<Condition> conds = criteria.getConditions();
		for (Condition cond : conds) {
			int op = cond.getOperator();
			switch(op) {
			case Condition.EQUALS:	
				Object value = cond.getValue();
				Assert.notNull(value, "Value is null for field: "+cond.getField());
				query.put(cond.getField(), value);
				break;
			case Condition.LIKE:	
				value = cond.getValue();
				Assert.notNull(value, "Value is null for field: "+cond.getField());
				Pattern pattern = Pattern.compile(".*"+value+"*", Pattern.CASE_INSENSITIVE);
				query.put(cond.getField(), pattern);
				break;
			case Condition.BETWEEN:
				Object[] values = cond.getValues();
				Assert.notNull(values, "Value is null for field: "+cond.getField());
				Assert.isTrue(values.length == 2, "Two values needed for field: "+cond.getField());
				Assert.notNull(values[0], "Value is null for field: "+cond.getField());
				Assert.notNull(values[1], "Value is null for field: "+cond.getField());
				
				query.put(cond.getField(), new BasicDBObject("$gt", values[0]).append("$lte", values[1]));
				break;
			default:
				throw new RuntimeException("Unknown operator: "+op);
			}
		}
		return query; 
	}

	public void clear(String table) {
		DBCollection coll = db.getCollection(table);
		coll.drop();
	}

}